package com.keqiang.filepicker

import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
import android.net.Uri
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.ImageView.ScaleType
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.bumptech.glide.load.MultiTransformation
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.load.resource.bitmap.*
import com.bumptech.glide.request.RequestOptions
import com.chad.library.adapter.base.RvQuickAdapter
import com.chad.library.adapter.base.viewholder.BaseViewHolder
import com.wgw.photo.preview.PhotoPreview
import com.wgw.photo.preview.ShapeTransformType
import com.wgw.photo.preview.interfaces.ImageLoader
import java.io.File
import java.text.SimpleDateFormat
import java.util.*


/**
 * 文件适配器
 *
 * @author Created by wanggaowan on 2022/5/18 16:07
 */
internal class FileAdapter(private val activity: FilePickerActivity, private val authority: String,
                           var isGridLayout: Boolean = false, fileList: List<File>? = null)
    : RvQuickAdapter<File, BaseViewHolder>(R.layout.fp_rv_item_file_or_folder, fileList) {

    private val imageRadius = (activity.resources.displayMetrics.density * 4).toInt()
    private val imagePadding = (activity.resources.displayMetrics.density * 5).toInt()
    private val roundedCorners = RoundedCorners(imageRadius)
    private val imageLoader = CustomImageLoader()
    private val dateFormat: SimpleDateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.CHINA)

    override val nestViewIds: IntArray?
        get() = null

    override fun disableScale(): Boolean {
        return true
    }

    override fun getDefItemViewType(position: Int): Int {
        if (!isGridLayout) {
            return NORMAL
        }

        val data = data[position]
        if (data.isDirectory || FilePickerActivity.PLACE_HOLDER_FOLDER == data.path) {
            return GRID_FOLDER
        }

        return GRID_FILE
    }

    override fun onCreateDefViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder {
        if (GRID_FILE == viewType) {
            return super.createBaseViewHolder(parent, R.layout.fp_rv_item_file_grid_layout)
        }

        if (GRID_FOLDER == viewType) {
            return super.createBaseViewHolder(parent, R.layout.fp_rv_item_folder_grid_layout)
        }

        return super.createBaseViewHolder(parent, R.layout.fp_rv_item_file_or_folder)
    }

    override fun convert(holder: BaseViewHolder, item: File) {
        val isDirectory = item.isDirectory || FilePickerActivity.PLACE_HOLDER_FOLDER == item.path
        val fileName = item.name
        holder.setText(R.id.tv_file_name, fileName)
        holder.itemView.visibility = View.VISIBLE
        if (isGridLayout && isDirectory) {
            if (FilePickerActivity.PLACE_HOLDER_FOLDER == item.path) {
                holder.itemView.visibility = View.INVISIBLE
            }
            return
        }

        val lastModified = dateFormat.format(Date(item.lastModified()))
        val size = if (isDirectory) "" else fileSizeFormat(item.length()) + " | "
        holder.setText(R.id.tv_file_desc, "$size$lastModified")

        val isCouldOpenImage = isCouldOpenImage(fileName)
        val ivIcon: ImageView = holder.getViewNonNull(R.id.iv_icon)
        val ivRight: ImageView =
            if (!isGridLayout) holder.getViewNonNull(R.id.iv_right) else holder.getViewNonNull(R.id.iv_open)
        val isChosen = activity.isFileChosen(item) != RecyclerView.NO_POSITION

        if (isDirectory) {
            ivRight.visibility = View.VISIBLE
            ivRight.setImageResource(R.drawable.fp_right_24)
            ivRight.isClickable = false
            ivRight.isEnabled = false
        } else {
            if (!isGridLayout) {
                ivRight.visibility = View.VISIBLE
                ivRight.setImageResource(R.drawable.fp_file_open_24)
                ivRight.isClickable = true
                ivRight.isEnabled = true
            }

            ivRight.setOnClickListener {
                if (!isCouldOpenImage) {
                    startThirdPartOpen(authority, item)
                    return@setOnClickListener
                }

                if (isGridLayout) {
                    val ivIconBig: ImageView = holder.getViewNonNull(R.id.iv_icon_big)
                    previewImage(ivIconBig, item)
                } else {
                    previewImage(ivIcon, item, true)
                }

            }
        }

        if (!isGridLayout) {
            if (isChosen) {
                ivIcon.isClickable = false
                ivIcon.setPadding(imagePadding, imagePadding, imagePadding, imagePadding)
                ivIcon.setImageResource(R.drawable.fp_check_circle_24)
                return
            }

            if (isCouldOpenImage || isCouldOpenVideo(fileName)) {
                ivIcon.setPadding(0, 0, 0, 0)
                if (isCouldOpenImage) {
                    ivIcon.setOnClickListener { previewImage(ivIcon, item, true) }
                } else {
                    ivIcon.isClickable = false
                }

                loadImage(ivIcon, item, roundedCorners,
                    if (isCouldOpenImage) R.drawable.fp_file_image else R.drawable.fp_file_video)
                return
            }

            ivIcon.isClickable = false
            ivIcon.setPadding(0, 0, 0, 0)
            if (isApk(fileName)) {
                ivIcon.setImageDrawable(getApkIcon(activity, item.path))
                return
            }

            ivIcon.setImageResource(getFileIcon(item))
            return
        }

        val ivIconBig: ImageView = holder.getViewNonNull(R.id.iv_icon_big)
        val ivIconSmall: ImageView = holder.getViewNonNull(R.id.iv_icon_small)
        val icon = getFileIcon(item)
        if (isCouldOpenImage || isCouldOpenVideo(fileName)) {
            ivIconSmall.visibility = View.GONE
            loadImage(ivIconBig, item)
        } else {
            ivIconBig.setImageDrawable(null)
            ivIconSmall.visibility = View.VISIBLE
            if (isApk(fileName)) {
                ivIconSmall.setImageDrawable(getApkIcon(activity, item.path))
            } else {
                ivIconSmall.setImageResource(icon)
            }
        }

        if (isChosen) {
            ivIcon.setImageResource(R.drawable.fp_check_circle_24)
        } else {
            if (isAudio(fileName)) {
                ivIcon.setImageResource(R.drawable.fp_icon_audio)
            } else if (isImage(fileName)) {
                ivIcon.setImageResource(R.drawable.fp_icon_image)
            } else if (isVideo(fileName)) {
                ivIcon.setImageResource(R.drawable.fp_file_video)
            } else if (isApk(fileName)) {
                ivIcon.setImageResource(R.drawable.fp_file_apk)
            } else {
                ivIcon.setImageResource(R.drawable.fp_icon_file)
            }
        }
    }

    /*
     * 采用了新的办法获取APK图标，之前的失败是因为android中存在的一个BUG,通过
     * appInfo.publicSourceDir = apkPath;来修正这个问题，详情参见:
     * http://code.google.com/p/android/issues/detail?id=9151
     */
    private fun getApkIcon(context: Context, apkPath: String): Drawable? {
        val pm: PackageManager = context.packageManager
        val info = pm.getPackageArchiveInfo(apkPath, PackageManager.GET_ACTIVITIES)
        if (info != null) {
            val appInfo = info.applicationInfo
            appInfo.sourceDir = apkPath
            appInfo.publicSourceDir = apkPath
            try {
                return appInfo.loadIcon(pm)
            } catch (e: Exception) {
                if (BuildConfig.DEBUG) {
                    e.printStackTrace()
                }
            }
        }
        return ContextCompat.getDrawable(context, R.drawable.fp_file_apk)
    }

    private fun loadImage(imageView: ImageView, file: File,
                          roundedCorners: RoundedCorners? = null,
                          errorImage: Int = -1) {
        val transforms: MutableList<BitmapTransformation> = ArrayList()
        when (imageView.scaleType) {
            ScaleType.CENTER_CROP -> {
                transforms.add(CenterCrop())
            }

            ScaleType.CENTER_INSIDE -> {
                transforms.add(CenterInside())
            }

            else -> {
                transforms.add(FitCenter())
            }
        }

        roundedCorners?.let {
            transforms.add(it)
        }

        var options = RequestOptions()
            .diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)
        options = options.transform(MultiTransformation(transforms))

        var glide = Glide.with(context)
            .asBitmap()
            .load(file)

        if (errorImage != -1) {
            glide = glide.error(errorImage)
        }

        glide.apply(options)
            .into(imageView)
    }

    private fun fileSizeFormat(size: Long): String {
        var v: Double = size * 1.0
        if (v < 1024) {
            return v.toInt().toString() + "B"
        }

        v /= 1024f
        if (v < 1024) {
            return v.toInt().toString() + "KB"
        }

        v /= 1024f
        if (v < 1024) {
            return v.toInt().toString() + "MB"
        }

        v /= 1024f
        return v.toInt().toString() + "GB"
    }

    private fun getFileIcon(file: File): Int {
        val name = file.name

        if (file.isDirectory) {
            return R.drawable.fp_folder_24
        }

        if (isText(name)) {
            return R.drawable.fp_file_txt
        }

        if (isXML(name)) {
            return R.drawable.fp_file_xml
        }

        if (isHTML(name)) {
            return R.drawable.fp_file_html
        }

        if (isJSON(name)) {
            return R.drawable.fp_file_json
        }

        if (isImage(name)) {
            return R.drawable.fp_icon_image
        }

        if (isApk(name)) {
            return R.drawable.fp_file_apk
        }

        if (isPDF(name)) {
            return R.drawable.fp_file_pdf
        }

        if (isWold(name)) {
            return R.drawable.fp_file_word
        }

        if (isPPT(name)) {
            return R.drawable.fp_file_ppt
        }

        if (isExcel(name)) {
            return R.drawable.fp_file_excel
        }

        if (isZip(name)) {
            return R.drawable.fp_file_zip
        }

        if (isVideo(name)) {
            return R.drawable.fp_file_video
        }

        if (isAudio(name)) {
            return R.drawable.fp_file_audio
        }

        return R.drawable.fp_file_unknow
    }

    private fun isDocument(name: String): Boolean {
        return isPDF(name)
            || isWold(name)
            || isPPT(name)
            || isExcel(name)
            || isText(name)
            || isXML(name)
            || isHTML(name)
            || isJSON(name)
    }

    private fun isPDF(name: String): Boolean {
        return name.endsWith(".pdf") || name.endsWith(".PDF")
    }

    private fun isWold(name: String): Boolean {
        return name.endsWith(".doc")
            || name.endsWith(".docx")
            || name.endsWith(".DOC")
            || name.endsWith(".DOCX")
    }

    private fun isPPT(name: String): Boolean {
        return name.endsWith(".ppt")
            || name.endsWith(".pptx")
            || name.endsWith(".PPT")
            || name.endsWith(".PPTX")
    }

    private fun isExcel(name: String): Boolean {
        return name.endsWith(".xls")
            || name.endsWith(".xlsx")
            || name.endsWith(".XLS")
            || name.endsWith(".XLSX")
    }

    private fun isZip(name: String): Boolean {
        return name.endsWith(".rar")
            || name.endsWith(".zip")
            || name.endsWith(".7z")
            || name.endsWith(".RAR")
            || name.endsWith(".ZIP")
            || name.endsWith(".7Z")
    }

    private fun isText(name: String): Boolean {
        return name.endsWith(".txt")
            || name.endsWith(".TXT")
    }

    private fun isXML(name: String): Boolean {
        return name.endsWith(".xml")
            || name.endsWith(".XML")
    }

    private fun isHTML(name: String): Boolean {
        return name.endsWith(".html")
            || name.endsWith(".HTML")
    }

    private fun isJSON(name: String): Boolean {
        return name.endsWith(".json")
            || name.endsWith(".JSON")
    }

    private fun isApk(name: String): Boolean {
        return name.endsWith(".apk")
            || name.endsWith(".APK")
    }

    private fun isMusic(name: String): Boolean {
        return name.endsWith(".mp3")
            || name.endsWith(".MP3")
    }

    private fun isAudio(name: String): Boolean {
        return isMusic(name)
            || name.endsWith(".mpga")
            || name.endsWith(".wav")
            || name.endsWith(".m4a")
            || name.endsWith(".amr")
            || name.endsWith(".awb")
            || name.endsWith(".ogg")
            || name.endsWith(".oga")
            || name.endsWith(".aac")
            || name.endsWith(".mka")
            || name.endsWith(".mid")
            || name.endsWith(".midi")
            || name.endsWith(".xmf")
            || name.endsWith(".rtttl")
            || name.endsWith(".smf")
            || name.endsWith(".imy")
            || name.endsWith(".rtx")
            || name.endsWith(".ota")
            || name.endsWith(".mxmf")
            || name.endsWith(".m3u")
            || name.endsWith(".m3u8")
            || name.endsWith(".MPGA")
            || name.endsWith(".WAV")
            || name.endsWith(".M4A")
            || name.endsWith(".AMR")
            || name.endsWith(".AWB")
            || name.endsWith(".OGG")
            || name.endsWith(".OGA")
            || name.endsWith(".AAC")
            || name.endsWith(".MKA")
            || name.endsWith(".MID")
            || name.endsWith(".MIDI")
            || name.endsWith(".XMF")
            || name.endsWith(".RTTTL")
            || name.endsWith(".SMF")
            || name.endsWith(".IMY")
            || name.endsWith(".RTX")
            || name.endsWith(".OTA")
            || name.endsWith(".MXMF")
            || name.endsWith(".M3U")
            || name.endsWith(".M3U8")
    }

    private fun isCouldOpenVideo(name: String): Boolean {
        return name.endsWith(".mp4")
            || name.endsWith(".avi")
            || name.endsWith(".3gp")
            || name.endsWith(".mkv")
            || name.endsWith(".mkv")
            || name.endsWith(".webm")
            || name.endsWith(".MP4")
            || name.endsWith(".AVI")
            || name.endsWith(".3GP")
            || name.endsWith(".MKV")
            || name.endsWith(".MKV")
            || name.endsWith(".WEBM")
    }

    private fun isVideo(name: String): Boolean {
        return isCouldOpenVideo(name)
            || name.endsWith(".mpeg")
            || name.endsWith(".mpg")
            || name.endsWith(".m4v")
            || name.endsWith(".mov")
            || name.endsWith(".3gpp")
            || name.endsWith(".3g2")
            || name.endsWith(".3gpp2")
            || name.endsWith(".ts")
            || name.endsWith(".MPEG")
            || name.endsWith(".MPG")
            || name.endsWith(".M4V")
            || name.endsWith(".MOV")
            || name.endsWith(".3GPP")
            || name.endsWith(".3G2")
            || name.endsWith(".3GPP2")
            || name.endsWith(".TS")
    }

    private fun isCouldOpenImage(name: String): Boolean {
        return name.endsWith(".png")
            || name.endsWith(".jpg")
            || name.endsWith(".gif")
            || name.endsWith(".webp")
            || name.endsWith(".jpeg")
            || name.endsWith(".PNG")
            || name.endsWith(".JPG")
            || name.endsWith(".GIF")
            || name.endsWith(".WEBP")
            || name.endsWith(".JPEG")
    }

    private fun isImage(name: String): Boolean {
        return isCouldOpenImage(name)
            || name.endsWith(".bmp")
            || name.endsWith(".wbmp")
            || name.endsWith(".webp")
            || name.endsWith(".heic")
            || name.endsWith(".heif")
            || name.endsWith(".dng")
            || name.endsWith(".cr2")
            || name.endsWith(".nef")
            || name.endsWith(".nrw")
            || name.endsWith(".arw")
            || name.endsWith(".rw2")
            || name.endsWith(".orf")
            || name.endsWith(".raf")
            || name.endsWith(".pef")
            || name.endsWith(".srw")
            || name.endsWith(".BMP")
            || name.endsWith(".WBMP")
            || name.endsWith(".WEBP")
            || name.endsWith(".HEIC")
            || name.endsWith(".HEIF")
            || name.endsWith(".DNG")
            || name.endsWith(".CR2")
            || name.endsWith(".NEF")
            || name.endsWith(".NRW")
            || name.endsWith(".ARW")
            || name.endsWith(".RW2")
            || name.endsWith(".ORF")
            || name.endsWith(".RAF")
            || name.endsWith(".PEF")
            || name.endsWith(".SRW")
    }

    private fun previewImage(view: View, file: File, needTransform: Boolean = false) {
        var preview = PhotoPreview.with(activity)
            .imageLoader(imageLoader)

        if (needTransform) {
            preview = preview.shapeTransformType(ShapeTransformType.ROUND_RECT)
                .shapeCornerRadius(imageRadius)
        }

        preview.sources(file)
            .fullScreen(true)
            .build()
            .show(view)
    }

    @SuppressLint("QueryPermissionsNeeded")
    private fun startThirdPartOpen(authority: String, file: File) {
        val openIntent = Intent(Intent.ACTION_VIEW)
        // Android 会自动将 CATEGORY_DEFAULT 类别应用于传递给 startActivity() 和 startActivityForResult() 的所有隐式 Intent
        // 因此此处指定/不指定Intent.CATEGORY_DEFAULT都可以，但是如果还要匹配其它类别则必须指定
        openIntent.addCategory(Intent.CATEGORY_DEFAULT)
        openIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        val uriForFile: Uri = try {
            FileProvider.getUriForFile(activity, authority, file)
        } catch (e: Exception) {
            Toast.makeText(activity, FilePicker.stringRes.filePreviewFailedHint, Toast.LENGTH_SHORT).show()
            return
        }

        // 设置intent的data和Type属性。
        openIntent.data = uriForFile
        if (openIntent.resolveActivity(activity.packageManager) == null) {
            // 如果没有可用的第三方应用，则不能调用startActivity(openIntent)，否则程序将崩溃
            Toast.makeText(activity, FilePicker.stringRes.noValidThirdAppForFilePreviewHint, Toast.LENGTH_SHORT).show()
            return
        }

        // 给接收方授予临时读取权限，这些权限会在接收方应用的任务堆栈完成时自动过期
        openIntent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
        // 跳转
        try {
            activity.startActivity(openIntent)
        } catch (e: Exception) {
            Toast.makeText(activity, FilePicker.stringRes.filePreviewFailedHint, Toast.LENGTH_SHORT).show()
        }
    }

    companion object {
        private const val NORMAL = 0
        private const val GRID_FILE = 1
        private const val GRID_FOLDER = 2
    }
}

private class CustomImageLoader : ImageLoader {
    override fun onLoadImage(position: Int, source: Any?, imageView: ImageView) {
        Glide.with(imageView.context)
            .asBitmap()
            .load(source as? File)
            .into(imageView)
    }
}
